#ifndef Thread_INCLUDED
#define Thread_INCLUDED
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <pthread.h>
#include <limits.h>
#include <errno.h>
#include <memory>
#include <string>
#include <time.h>

#include "event.h"
#include "mutex.h"
#include "runnable.h"
#include "signalhandler.h"
#include "timestat.h"

using std::string;

class Thread
{
public: 
    Thread();

    Thread(const string& name);

    ~Thread();

    pid_t tid();

    pthread_t id();

    const string& name();

    void setStackSize(int size);

    int getStackSize() const;

    void start(Runnable& target);

    void join();

    bool join(long milliseconds);

    bool tryJoin(long milliseconds);

    bool isRunning() const;

    static void sleep(long milliseconds);

    static void yield();

    // 线程调度入口
    static void* runnableEntry(void* pThread);

private:
    Thread(const Thread&);
    Thread& operator = (const Thread&);

private:
    // ids
    pid_t           m_tid;
    pthread_t       m_id;
    string          m_name;

    // thread fix info
    Runnable*       m_pTarget;
    int             m_prio;
    int             m_osPrio;
    int             m_policy;
    std::size_t     m_stackSize;

    // running ctrl
    bool            m_started;
    bool            m_joined;
    mutable Mutex   m_mutex;
    Event           m_done;
};


//
// inlines
//

inline pid_t Thread::tid()
{
    // return gettid();
    return syscall(SYS_gettid);
}

inline pthread_t Thread::id()
{
    return m_id;
}

inline const string& Thread::name()
{
    return m_name;
}

inline bool Thread::isRunning() const
{
    return m_pTarget != NULL;
}

inline void Thread::sleep(long milliseconds)
{
    int rc = 0;
    struct timespec ts;
    ts.tv_sec   = milliseconds / 1000;
    ts.tv_nsec  = (milliseconds - ts.tv_sec*1000)*1000000;
    CTimeStat tmstat;
    tmstat.AddStep("1");
    long remain = 0;
    do
    {
        rc = ::nanosleep(&ts, 0);
        if (rc < 0 && errno == EINTR)
        {
            tmstat.AddStep("2");
            long waited = tmstat.GetInterval("2","1",UNIT_USECOND);
            remain = milliseconds - waited/1000;
        }
    }
    while (remain > 0 && rc < 0 && errno == EINTR);
}

inline void Thread::yield()
{
    sched_yield();
}

inline void Thread::setStackSize(int size)
{
    m_stackSize = size;
}

inline int Thread::getStackSize() const
{
    return m_stackSize;
}

#endif



